#include <stdio.h>
#include <termios.h>
#include <string.h>  
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <errno.h>

#include "soft_types.h"
#include "Uart/Uart.h"
#include "LinuxMsg/LinuxMsg.h"

typedef struct __COMM_UART_INFO_s__
{
    INT32 iUartFd;
    UART_READ_CB_F pfCallBack;
}COMM_UART_INFO_s;

typedef struct __COMM_UART_CTRL_s__
{
    LINUX_MSG_s stQueue;
    pthread_t stRecMsg;
    pthread_t stProcMsg;
    pthread_mutex_t stLock;
    COMM_UART_INFO_s astUart[COMM_UART_MAX];
}COMM_UART_CTRL_s;

#ifdef COMM_MULTI_UART
static COMM_UART_CTRL_s gstUart;
#endif

/**********************************************************
 * Function Name:        CommUart_cfg
 * Description:          串口配置
 * Parameters[in]:       IN INT32 fd
 *                       IN UART_CFG_PARAM_s stCf
 * Parameters[out]:      void
 * ReturnValue:          ULONG
 * Author:               liuke
 * Date:                 2020.05.16
***********************************************************/
ULONG CommUart_cfg(IN INT32 fd, IN UART_CFG_PARAM_s stCfg)  
{   
    INT32   i;
    INT32   speed_arr[] = {B115200, B57600, B38400, B19200, B9600, B4800, B2400, B1200, B300};  
    INT32   name_arr[] = {115200, 57600, 38400, 19200, 9600, 4800, 2400, 1200, 300};          
    struct termios options;  
       
    if(tcgetattr(fd, &options) != 0)  
    {  
        perror("SetupSerial 1");      
        return STAT_ERR_FILE_TCGET;   
    }  

    for ( i= 0;  i < sizeof(speed_arr) / sizeof(INT32);  i++)  
    {  
        if  (stCfg.baud_rate == name_arr[i])  
        {               
            cfsetispeed(&options, speed_arr[i]);   
            cfsetospeed(&options, speed_arr[i]);    
        }  
    }       
     
    options.c_cflag |= CLOCAL;
    options.c_cflag |= CREAD;  
    
    switch(stCfg.flow_ctrl)  
    {
        case 0 :
        {
            options.c_cflag &= ~CRTSCTS;  
            break;     
        }
        
        case 1 :
        {
            options.c_cflag |= CRTSCTS;  
            break;
        }
        
        case 2 :
        {
            options.c_cflag |= IXON | IXOFF | IXANY;  
            break;
        }
        
        default:
        {
            fprintf(stderr,"Unsupported flow ctrl\n");  
            return STAT_ERR_INVAILD_PARAM; 
            break;
        }
    }  

    options.c_cflag &= ~CSIZE;  
    switch (stCfg.data_bits)  
    {    
        case 5:  
            options.c_cflag |= CS5;  
            break;
            
        case 6:  
            options.c_cflag |= CS6;  
            break;
            
        case 7:      
            options.c_cflag |= CS7;  
            break;
            
        case 8:      
            options.c_cflag |= CS8;  
            break;    

        default:     
            fprintf(stderr,"Unsupported data size\n");  
            return STAT_ERR_INVAILD_PARAM;   
    }  

    switch (stCfg.parity)  
    {    
        case 'n':
        case 'N':
        {
            options.c_cflag &= ~PARENB;   
            options.c_iflag &= ~INPCK;      
            break;   
        }
        
        case 'o':
        case 'O':
        {
            options.c_cflag |= (PARODD | PARENB);   
            options.c_iflag |= INPCK;               
            break;   
        }
        
        case 'e':
        case 'E':    
        {
            options.c_cflag |= PARENB;         
            options.c_cflag &= ~PARODD;         
            options.c_iflag |= INPCK;        
            break;  
        }
        
        case 's':  
        case 'S':
        {
            options.c_cflag &= ~PARENB;  
            options.c_cflag &= ~CSTOPB;  
            break;   
        }
        
        default:    
        {
            fprintf(stderr,"Unsupported parity\n");      
            return STAT_ERR_INVAILD_PARAM; 
            break;
        }
    }   
 
    switch (stCfg.stop_bits)  
    {
        case 1: 
        {
            options.c_cflag &= ~CSTOPB;
            break;
        }
        
        case 2:     
        {
            options.c_cflag |= CSTOPB;
            break;
        }
        
        default:     
        {
            fprintf(stderr,"Unsupported stop bits\n");   
            return STAT_ERR_INVAILD_PARAM;  
            break;
        }
    }  
     
    options.c_oflag &= ~OPOST;  
    
    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);  

    options.c_iflag &= ~(BRKINT | ICRNL| IGNCR | INPCK | ISTRIP | IXON);

    options.c_cc[VTIME] = 1;   
    options.c_cc[VMIN] = 1; 
     
    tcflush(fd,TCIFLUSH);  
     
    if (tcsetattr(fd,TCSANOW,&options) != 0)    
    {  
        perror("com set error!\n");    
        return STAT_ERR_FILE_TCSET;   
    }
    
    return STAT_SUCCEED;   
}  

/**********************************************************
 * Function Name:        CommUart_open
 * Description:          打开串口
 * Parameters[in]:       IN UART_CFG_PARAM_s stCfg
 * Parameters[out]:      OUT INT32 *pFd              
 * ReturnValue:          ULONG
 * Author:               liuke
 * Date:                 2020.05.16
***********************************************************/
ULONG CommUart_open(IN UART_INIT_PARAM_s stCfg, OUT INT32 *pFd)  
{
    if (pFd == NULL || stCfg.pcPath == NULL)
    {
        return STAT_ERR_INVAILD_PARAM;
    }

    *pFd = open(stCfg.pcPath, O_RDWR | O_NOCTTY | O_NDELAY);  
    if ((*pFd) < 0)  
    {  
        perror("Can't Open Serial Port");  
        return STAT_ERR_FILE_OPEN;  
    }
	                          
    if(fcntl(*pFd, F_SETFL, 0) < 0)  
    {  
        printf("fcntl failed!\n");  
        return STAT_ERR_FILE_FCNTL;  
    }       
	   
    if(isatty(*pFd) == 0)  
    {  
        printf("standard input is not a terminal device\n");  
        return STAT_ERR_UART_TYPE;  
    }

    if (CommUart_cfg(*pFd, stCfg.stConfig) != STAT_SUCCEED)
    {
        return STAT_ERR_UART_CFG;
    }

#ifdef COMM_MULTI_UART
    INT32 i = 0;
    
    for (i = 0; i < COMM_UART_MAX; i++)
    {
        if (0xFFFFFFFF == gstUart.astUart[i].iUartFd)
        {
            pthread_mutex_lock(&gstUart.stLock);
            gstUart.astUart[i].iUartFd = *pFd;
            gstUart.astUart[i].pfCallBack = stCfg.pfReadCB;
            pthread_mutex_unlock(&gstUart.stLock);
            break;
        }
    }

    if (i >= COMM_UART_MAX)
    {
        CommUart_close(*pFd);
        *pFd = 0;
        return STAT_ERR_UART_ADD;
    }
#endif

    printf("[%s %d] Uart open %s fd %d success\n", __FUNCTION__, __LINE__, 
           stCfg.pcPath, *pFd);
    
    return STAT_SUCCEED;  
}   

/**********************************************************
 * Function Name:        CommUart_close
 * Description:          关闭串口
 * Parameters[in]:       IN UART_CFG_PARAM_s stCfg
 * Parameters[out]:      OUT INT32 *pFd              
 * ReturnValue:          void
 * Author:               liuke
 * Date:                 2020.05.16
***********************************************************/
void CommUart_close(IN INT32 fd)  
{
#ifdef COMM_MULTI_UART
    INT32 i = 0;
    
    for (i = 0; i < COMM_UART_MAX; i++)
    {
        if (fd == gstUart.astUart[i].iUartFd)
        {
            pthread_mutex_lock(&gstUart.stLock);
            memset(&gstUart.astUart[i], 0xFF, sizeof(gstUart.astUart[i]));
            pthread_mutex_unlock(&gstUart.stLock);
        }
    }
#endif

    close(fd);

    return;
}

/**********************************************************
 * Function Name:        CommUart_recv
 * Description:          串口读取
 * Parameters[in]:       IN INT32 fd
                         IN INT32 data_len
 * Parameters[out]:      OUT UINT8 *rcv_buf
                         OUT INT32 *recvLen
 * ReturnValue:          ULONG
 * Author:               liuke
 * Date:                 2020.05.16
***********************************************************/
ULONG CommUart_recv(IN INT32 fd, IN INT32 data_len, OUT UINT8 *rcv_buf, 
                        OUT INT32 *recvLen)  
{  
    INT32 len,fs_sel;  
    fd_set fs_read;  
     
    struct timeval time;  
     
    FD_ZERO(&fs_read);  
    FD_SET(fd,&fs_read);  
     
    time.tv_sec = 0;  
    time.tv_usec = 1000;  
   
    fs_sel = select(fd + 1, &fs_read, NULL, NULL, &time);  
    if(fs_sel <= 0)
    {
        return STAT_ERR_SELECT; 
    }  
    else  
    {  
        if(FD_ISSET(fd,&fs_read))
        {
            len = read(fd, rcv_buf, data_len); 
            *recvLen = len;
            return STAT_SUCCEED;
        }
        else
        {
            return STAT_ERR_SELECT_NO_DATA;
        }
    }
}  

/**********************************************************
 * Function Name:        CommUart_send
 * Description:          串口发送
 * Parameters[in]:       IN INT32 fd
                         IN UINT8 *send_buf
                         IN INT32 data_len
 * Parameters[out]:      void
 * ReturnValue:          ULONG
 * Author:               liuke
 * Date:                 2020.05.16
***********************************************************/
ULONG CommUart_send(IN INT32 fd, IN UINT8 *send_buf, IN INT32 data_len)  
{  
    INT32 len = 0;  
     
    len = write(fd,send_buf,data_len);  
    if (len != data_len )  
    {
        tcflush(fd, TCOFLUSH);  
        return STAT_ERR_UART_WRITE;  
    }

    return STAT_SUCCEED;
}

#ifdef COMM_MULTI_UART
/**********************************************************
 * Function Name:        CommUart_recvProc
 * Description:          multi 管理串口：循环检测串口是否有数据进入
 * Parameters[in]:       void * pParam
 * Parameters[out]:      void
 * ReturnValue:          UINT32
 * Author:               liuke
 * Date:                 2020.05.16
***********************************************************/
void *CommUart_recvProc(void * pParam)
{
    UINT8 szBuf[STRING_LEN_1024] = {0};
    INT32 i = 0;
    INT32 len = 0;
    INT32 fs_sel = 0;
    INT32 iFdMax = 0;
    fd_set fs_read;  
    struct timeval time;  
    LINUX_MSG_ITEM_s *pstMsg = (LINUX_MSG_ITEM_s *)szBuf;

    while (1)
    {
        FD_ZERO(&fs_read);
        pthread_mutex_lock(&gstUart.stLock);
        for (i = 0; i < COMM_UART_MAX; i++)
        {
            if (gstUart.astUart[i].iUartFd != 0xFFFFFFFF)
            {
                FD_SET(gstUart.astUart[i].iUartFd, &fs_read);
                if (iFdMax < (gstUart.astUart[i].iUartFd))
                {
                    iFdMax = gstUart.astUart[i].iUartFd;
                }
            }

        }
        
        pthread_mutex_unlock(&gstUart.stLock);

        time.tv_sec = 1;  
        time.tv_usec = 0;  
       
        fs_sel = select(iFdMax + 1, &fs_read, NULL, NULL, &time);  
        if(fs_sel <= 0)
        {
            continue; 
        }  

        pthread_mutex_lock(&gstUart.stLock);
        for (i = 0; i < COMM_UART_MAX; i++)
        {
            if ((gstUart.astUart[i].iUartFd != 0xFFFFFFFF) &&
                (FD_ISSET(gstUart.astUart[i].iUartFd,&fs_read)))
            {
                len = read(gstUart.astUart[i].iUartFd, pstMsg->stData.aucMsgData, 
                                        sizeof(szBuf) - sizeof(*pstMsg)); 
                pstMsg->lType = i + 1;
                pstMsg->stData.ulLen = len;
                gstUart.stQueue.send(gstUart.stQueue.iMsgQueueId, pstMsg);
                
                memset(szBuf, 0, sizeof(szBuf));
            }
        }
        pthread_mutex_unlock(&gstUart.stLock);
    }
}

/**********************************************************
 * Function Name:        CommUart_Proc
 * Description:          multi 管理串口：执行回调函数
 * Parameters[in]:       void * pParam
 * Parameters[out]:      void
 * ReturnValue:          UINT32
 * Author:               liuke
 * Date:                 2020.05.16
***********************************************************/
void *CommUart_Proc(void * pParam)
{
    UINT8 szBuf[STRING_LEN_1024] = {0};
    ULONG uRet = STAT_SUCCEED;
    LINUX_MSG_ITEM_s *pstMsg = (LINUX_MSG_ITEM_s *)szBuf;

    while (1)
    {
        pstMsg->lType = 0;
        pstMsg->stData.ulLen = sizeof(szBuf) - sizeof(pstMsg->lType);
        uRet = gstUart.stQueue.recv(gstUart.stQueue.iMsgQueueId, pstMsg);
        if (uRet != STAT_SUCCEED)
        {
            continue;
        }

        pthread_mutex_lock(&gstUart.stLock);
        if ((pstMsg->lType <= COMM_UART_MAX) &&
            (gstUart.astUart[pstMsg->lType - 1].pfCallBack != NULL))
        {
            gstUart.astUart[pstMsg->lType - 1].pfCallBack(pstMsg->stData.aucMsgData, 
				pstMsg->stData.ulLen);
        }
        pthread_mutex_unlock(&gstUart.stLock);
   }
}

/**********************************************************
 * Function Name:        CommUart_init
 * Description:          multi 管理串口初始化
 * Parameters[in]:       void
 * Parameters[out]:      void
 * ReturnValue:          ULONG
 * Author:               liuke
 * Date:                 2020.05.16
***********************************************************/
ULONG CommUart_init(void)
{
    INT32 iRet = 0;
    ULONG uRet = STAT_SUCCEED;

    memset(gstUart.astUart, 0xFF, sizeof(gstUart.astUart));

    pthread_mutex_init(&gstUart.stLock, NULL);

    LINUX_MSG_init(&gstUart.stQueue);

    uRet = gstUart.stQueue.create('U', &gstUart.stQueue);
    if (uRet != STAT_SUCCEED)
    {
        return uRet;
    }

    /* create pthread */
    iRet = pthread_create(&gstUart.stRecMsg, NULL, CommUart_recvProc, NULL);
    if (iRet != 0)
    {
        return STAT_ERR_THREAD_CREATE;
    }

    iRet = pthread_detach(gstUart.stRecMsg);
    if (iRet != 0)
    {
        return STAT_ERR_THREAD_DETACH;
    }

    /* create pthread */
    iRet = pthread_create(&gstUart.stProcMsg, NULL, CommUart_Proc, NULL);
    if (iRet != 0)
    {
        return STAT_ERR_THREAD_CREATE;
    }

    iRet = pthread_detach(gstUart.stProcMsg);
    if (iRet != 0)
    {
        return STAT_ERR_THREAD_DETACH;
    }

    return uRet;
}

/**********************************************************
 * Function Name:        CommUart_destory
 * Description:          multi 管理串口销毁
 * Parameters[in]:       void
 * Parameters[out]:      void
 * ReturnValue:          UINT32
 * Author:               liuke
 * Date:                 2020.05.16
***********************************************************/
void CommUart_destory(void)
{
    INT32 iRet = 0;
    ULONG uRet = STAT_SUCCEED;

    iRet = pthread_cancel(gstUart.stRecMsg);
    if (iRet != 0)
    {
        printf("[%s %d] err %d [%s]\n", __FUNCTION__, __LINE__, errno, strerror(errno));
    }

    iRet = pthread_cancel(gstUart.stProcMsg);
    if (iRet != 0)
    {
        printf("[%s %d] err %d [%s]\n", __FUNCTION__, __LINE__, errno, strerror(errno));
    }

    uRet = gstUart.stQueue.RemoveID(&gstUart.stQueue);
    if (uRet != STAT_SUCCEED)
    {
        printf("[%s %d] MsgQueue_destory fail %lx\n", __FUNCTION__, __LINE__, uRet);
    }

    pthread_mutex_destroy(&gstUart.stLock);
}
#endif

